/* Generator_Facilities.h
 *
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */
#ifndef _Generator_facilities_
#define _Generator_facilities_
/*
	A set of classes to ease implementation of random generators.
*/
#include <src/miscellaneous/OS_basic.h>
#include <src/data/OS_Multi_Array.h>
#include <list>
/*! A proxy to fill a vector with random draws.
*/
	template <class generator>
	struct Generator_facilities
	{
		template <class Iterator>
		void fill(Iterator _First, Iterator _Last) {
			for (Iterator b = _First; b != _Last; ++b)
			*b = (static_cast< generator&>(*this)) ();
		}
//!		A call to fill(x) will fill the vector x with generator draws
		template <class T>
		void fill(T &t) {
			fill(t.begin(), t.end());
		}
//!		A virtual function, that have to be overloaded for generators depending on the number of draws.
		virtual void set_steps(OS_size nb) {
			steps_ = nb;
		};
		virtual OS_size get_steps() {return steps_;}
		OS_size steps_;
	};
/*! Composition of a random generator with a function.

	This is a multi-purpose class used to compose a function with a generator. 
	The default are a sobol generator together with an inverse cumulative function.
*/
template <class Generator_type = sobol_generator<32>, class function_type = OS_inverse_cumulative_normal_distribution >
class CompoundGenerator : public Generator_facilities<CompoundGenerator<Generator_type,function_type>>
 {
 public:
	typedef Generator_type	generator_type;
	typedef function_type	function_type;
	typedef OS_double result_type;
	CompoundGenerator(){};
	CompoundGenerator(const Generator_type& generator, const function_type & init = 0 ) : generator_(generator)
	{
		if (&init)
			function_ = init;
	};
	OS_double operator()() // next sampling
	{
		return function_(generator_());
	};
	virtual void set_steps(OS_size nb) {
		generator_.set_steps(nb);
	};
	virtual OS_size get_steps() {return generator_.get_steps();}

	 Generator_type generator_;
	 function_type function_;
 };

/*! Trivial Generator

	Given a vector\f$ \{x_i\}_{i=1,..,N}\f$, it this generator defines the identity operator : it returns at each call

\f[
	x_{i^{++}}
\f]
*/
template <class data, OS_size Dim = data::Dim>
struct TrivialGenerator : public Generator_facilities<TrivialGenerator<data>>
 {
 public:
	typedef TrivialGenerator						type;
	typedef typename data::base_value_type			base_value_type;
	typedef OS_Vector<base_value_type>				result_type;
	typedef typename data::ptr_type					data_iterator;
	typedef typename result_type::ptr_type			result_iterator;
	type () : sample_(Dim-1), it_data_(0),capacity_0_(0)
	{
		it_sample_	=	sample_.first_;
		set_steps(capacity_0_);
	};
	type (const type & generator) : data_(generator.data_), sample_(Dim-1) 
	{
		capacity_0_ =	data_.capacity(Dim-1);
		set_steps(capacity_0_);
		it_data_	=	data_.first_;
		it_sample_	=	sample_.first_;
	};
	type& operator = (const type& from)
	{
		data_		=	from.data_;
		sample_		=	from.sample_;
		capacity_0_ =	from.capacity_0_;
		set_steps(capacity_0_);
		it_data_	=	data_.first_;
		it_sample_	=	sample_.first_;
		return *this;
	};

	TrivialGenerator( const data& from) : data_(from), sample_(data::Dim) 
	{
		capacity_0_ =	data_.capacity(Dim-1);
		it_data_	=	data_.first_;
		it_sample_	=	sample_.first_;
		set_steps(capacity_0_);
	};
	void set_data( const data& from){
		data_		=	from;
		capacity_0_ =	data_.capacity(Dim-1);
		set_steps(capacity_0_);
		data_		=	from;
		it_data_	=	data_.first_;
	};
	result_type operator()() // next sampling
	{
		for (OS_size i=0; i < Dim-1; ++i) {
			*(it_sample_+i) = *(it_data_+i*capacity_0_);
		};
		++it_data_;
		return sample_;
	};
	OS_size			capacity_0_;
	data			data_;
	result_type		sample_;
	data_iterator	it_data_;
	result_iterator	it_sample_;
 };
/*! Trivial Generator. Specialization to the one dimensional case.

	Given a vector\f$ \{x_i\}_{i=1,..,N}\f$, it this generator defines the identity operator : it returns at each call
\f[
	x_{i^{++}}
\f]
	It is specialized to the one dimensional case.
*/
template <class data>
struct TrivialGenerator<data,1> : public Generator_facilities<TrivialGenerator<data>>
 {
 public:
	typedef typename data::value_type				result_type;
	typedef typename data::const_iterator			const_iterator;
	TrivialGenerator () {
		it_=data_.begin();
	};
	TrivialGenerator (const TrivialGenerator & generator) : data_(generator.data_) {
		it_=data_.begin();
		set_steps(data_.size());
	};
	TrivialGenerator( const data& from) : data_(from){
		it_=data_.begin();
		set_steps(data_.size());
	};
	void set_data( const data& from){
		data_=from;
		it_=data_.begin();
		set_steps(data_.size());
	};
	result_type operator()() // next sampling
	{
		return *(it_++);
	};
	 const_iterator it_;
	 data data_;
 };
/*! List of generators.

	Given a generator, this class build a generator of size n that is a list of generator.
*/
template <class result_container, class generator_type>
class generator_list : public std::list<generator_type>,
	public Generator_facilities<generator_list<result_container,generator_type> >
{
public :
	typedef typename result_container				result_type;
	typedef typename result_container::iterator		result_iterator;
	typedef std::list<generator_type>				Base;
public :
	generator_list(){};
	generator_list(size_t n) : samples_(n)
	{
		for (OS_size i=0;i<n;++i)
			push_back(generator_type());
	}
	virtual const result_type& operator()() {
		result_iterator itr=samples_.begin();
		iterator it = begin(), it_end= end();
		for (;it != it_end;++it,++itr)
			*itr = (*it)();
		return samples_;
	};
	result_container samples_;
};
/*! helper: construct a generator gluing together a quantile time dependant mapping and a uniform generator.
	the payoff function give the time. It must support the function payoff::get_maturity()
*/
template<class payoff, class optimal_transport_map, class uniform_generator>
struct process_generator : 
	public CompoundGenerator<uniform_generator,optimal_transport_map> 
{
	typedef CompoundGenerator									generator_type;
	typedef payoff												payoff_type;
	typedef typename optimal_transport_map::stochastic_process_type
																stochastic_process_type;
	typedef typename stochastic_process_type::underlying_type	underlying_type;
	typedef typename stochastic_process_type::matrix_type		matrix_type;

	process_generator(){};
	process_generator(const process_generator & gen) : process_(gen.process_), payoff_(gen.payoff_){};
	process_generator(smart_ptr<stochastic_process_type> & process, smart_ptr<payoff_type> & payoff)
		: payoff_(payoff), process_(process)
	{
	};
	void set_steps (unsigned long Samples_nb) {
		function_ = generator_type::function_type(process_,payoff_->get_maturity());
		generator_.set_steps(Samples_nb);
	};
	smart_ptr<payoff_type>				payoff_;
	smart_ptr<stochastic_process_type> process_;
};

#endif